﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Pool;
using DanMuHelper.Audio;
using DanMuHelper.Logging;
using Unity.Collections;
using UnityEngine.Audio;

namespace PickMeUp
{
    public class LoopAudioGroup
    {
        public int MaxPlayCount;

        public int CurPlayingCount;

        private AudioSource[] audioSources;

        private string _title;

        public LoopAudioGroup(int maxCount, string title = "")
        {
            _title = title;
            MaxPlayCount = maxCount;
            audioSources = new AudioSource[MaxPlayCount];
            for (int i = 0; i < MaxPlayCount; i++)
            {
                var audioSource = GameObject.Instantiate(Resources.Load<AudioSource>("Audio/AudioCtrlPrefab"));
                audioSource.name = title;
                audioSource.gameObject.SetActive(true);
                audioSource.playOnAwake = false;
                audioSource.loop = true;
                audioSources[i] = audioSource;
            }
        }

        public void PlayAudio(AudioClip clip, float volume)
        {
            if (CurPlayingCount < MaxPlayCount)
            {
                audioSources[CurPlayingCount].volume = volume;
                audioSources[CurPlayingCount].clip = clip;
                audioSources[CurPlayingCount].Play();
            }
            CurPlayingCount++;
            //Color.green.LogInfo($"LoopAudio - 播放循环音效：{_title}，当前播放数量：{CurPlayingCount}，最大播放数量：{MaxPlayCount}");
        }

        public void StopAudio()
        {
            if (CurPlayingCount <= 0) return;

            CurPlayingCount--;
            if (CurPlayingCount < MaxPlayCount)
            {
                audioSources[CurPlayingCount].Stop();
            }
            //Color.green.LogInfo($"LoopAudio - 关闭循环音效：{_title}，当前播放数量：{CurPlayingCount}，最大播放数量：{MaxPlayCount}");
        }
    }

    public class ShotAudioSource : IDisposable
    {
        private readonly int maxCapacity = 5;

        private AudioSource audioSource;

        private Queue<float> _shotAudioEndTime;
        private Stack<float> _shotAudioEndTimeStack;

        public ShotAudioSource(string title)
        {
            _shotAudioEndTime = new(maxCapacity);
            _shotAudioEndTimeStack = new(maxCapacity);
            audioSource = GameObject.Instantiate(Resources.Load<AudioSource>("Audio/AudioCtrlPrefab"));
            audioSource.gameObject.SetActive(true);
            audioSource.name = title;
            audioSource.playOnAwake = false;
            audioSource.loop = false;
        }

        public void PlayAudio(AudioClip clip, float volume, float intervalTime = 0)
        {
            float curTime = Time.realtimeSinceStartup;
            if (intervalTime > 0)
            {
                if (_shotAudioEndTimeStack.Count > 0
                    && curTime - _shotAudioEndTimeStack.Peek() <= intervalTime)
                {
                    //Color.blue.LogInfo($"ShotAudio - {clip.name} 终止此次播放：播放间隔过快");
                    return;
                }
            }
            float endTime = clip.length + curTime;
            _shotAudioEndTime.Enqueue(endTime);
            _shotAudioEndTimeStack.Push(endTime);
            audioSource.PlayOneShot(clip, volume);
            //Color.blue.LogInfo($"ShotAudio - {clip.name} 开始播放");
        }

        public void TryReleaseCapacity()
        {
            if (_shotAudioEndTime.Count <= 0) return;

            float curTime = Time.realtimeSinceStartup;
            for (int i = 0; i < _shotAudioEndTime.Count; i++)
            {
                if (curTime >= _shotAudioEndTime.Peek())
                {
                    _shotAudioEndTime.Dequeue();
                    _shotAudioEndTimeStack.Pop();
                }
                else break;
            }
        }

        public bool IsReachCapacityLimit()
        {
            return _shotAudioEndTime.Count >= maxCapacity;
        }

        public int GetPlayShotAudioNumber()
        {
            return _shotAudioEndTime.Count;
        }

        public void Dispose()
        {
            GameObject.Destroy(audioSource);
            _shotAudioEndTime.Clear();
            _shotAudioEndTimeStack.Clear();
        }
    }

    public class ShotAudioGroup
    {
        private List<ShotAudioSource> _objects;

        private ObjectPool<ShotAudioSource> _cache;

        private readonly string _title;

        private int totalShotAudioSourceNum;

        public ShotAudioGroup(string title = "")
        {
            _title = title;
            _objects = new(10);
            _cache = new(() =>
            {
                totalShotAudioSourceNum++;
                return new ShotAudioSource($"{_title}_{totalShotAudioSourceNum}");
            }
            , (obj) => { }, (obj) => { }, (obj) => { obj?.Dispose(); });
        }

        public void PlayAudio(AudioClip clip, int maxPlayNum, float volume, float interval = 0)
        {
            if (_objects.Count <= 0)
            {
                var shotAudioSource = _cache.Get();
                shotAudioSource.PlayAudio(clip, volume, interval);
                _objects.Add(shotAudioSource);
            }
            else
            {
                int curPlayTotalNum = 0;
                for (int i = 0; i < _objects.Count; i++)
                {
                    curPlayTotalNum += _objects[i].GetPlayShotAudioNumber();
                }
                if (curPlayTotalNum > maxPlayNum)
                {
                    //Color.blue.LogInfo($"ShotAudio - {clip.name} 终止此次播放：同时播放数量抵达上限{curPlayTotalNum}/{maxPlayNum}");
                    return;
                }

                var shotAudioSource = _objects.Find(x => !x.IsReachCapacityLimit());
                if (shotAudioSource == null)
                {
                    shotAudioSource = _cache.Get();
                    _objects.Add(shotAudioSource);
                }
                shotAudioSource.PlayAudio(clip, volume, interval);
            }
        }

        public void Trim()
        {
            for (int i = 0; i < _objects.Count; i++)
            {
                _objects[i].TryReleaseCapacity();
            }
        }
    }

    public class AudioController : MonoBehaviour
    {
        private readonly Dictionary<string, ShotAudioGroup> _shotAudioGroups = new(128);
        private readonly Dictionary<string, LoopAudioGroup> _loopAudioGroups = new(128);

        public void PlayOneShotAudio(string audioResName, int maxPlayNum = 10, float interval = 0, float volume = 1)
        {
            var audio = Resources.Load<AudioClip>($"Audio/Clips/{audioResName}");
            if (audio != null)
            {
                if (!_shotAudioGroups.ContainsKey(audioResName))
                {
                    _shotAudioGroups.Add(audioResName, new ShotAudioGroup($"AudioCtrl_{audioResName}"));
                }
                _shotAudioGroups[audioResName].PlayAudio(audio, maxPlayNum, volume, interval);
            }
        }

        public void PlayLoopAudio(string audioResName, int maxCount = 3, float volume = 1)
        {
            var audio = Resources.Load<AudioClip>($"Audio/Clips/{audioResName}");
            if (audio != null)
            {
                if (!_loopAudioGroups.ContainsKey(audioResName))
                {
                    _loopAudioGroups.Add(audioResName, new LoopAudioGroup(maxCount, $"AudioCtrl_{audioResName}"));
                }
                _loopAudioGroups[audioResName].PlayAudio(audio, volume);
            }
        }

        public void TryStopLoopAudio(string audioResName)
        {
            if (_loopAudioGroups.ContainsKey(audioResName))
            {
                _loopAudioGroups[audioResName].StopAudio();
            }
        }

        public void LateUpdate()
        {
            foreach (var item in _shotAudioGroups)
            {
                item.Value.Trim();
            }
        }
    }
}
